//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;

namespace Eas.WebCrawler.Interfaces {
    /// <summary>A request which represents a <see cref="Uri"/> and all the links processed to get to that <see cref="Uri"/>.</summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes")]
    public class Request : IComparable<Request>, IComparable {
        /// <summary>The parent of this request.</summary>
        /// <remarks>Backing field for the Parent property.</remarks>
        /// <seealso cref="Parent"/>
        private Request _Parent;

        /// <summary>The <see cref="System.Uri"/> of this request.</summary>
        /// <remarks>Backing field for the Uri property.</remarks>
        /// <seealso cref="Uri"/>
        private Uri _Uri;

        /// <summary>Create a <see cref="Request"/> from a <see cref="Uri"/> and a parent <see cref="Request"/>.</summary>
        /// <param name="uri">The <see cref="Uri"/> which this request represents.</param>
        /// <param name="parent">The request which linked to this <see cref="Uri"/>, or <code>null</code> if this is an initial request.</param>
        public Request(Uri uri, Request parent) {
            if (uri == null) throw new ArgumentNullException("uri");

            _Parent = parent;
            _Uri = JoinUrisIfNecessary(uri, parent);
            if(!_Uri.IsAbsoluteUri)
                throw new ArgumentOutOfRangeException("uri", Uri, "Uri can not be relative if it has not parent.");
        }

        /// <summary>Create a <see cref="Request"/> from a <see cref="Uri"/> and a parent <see cref="Request"/>.</summary>
        /// <param name="uri">The <see cref="Uri"/> which this request represents.</param>
        public Request(Uri uri) : this(uri, null) { }
        /// <summary>Create a <see cref="Request"/> from a string representation of a <see cref="Uri"/> and a parent <see cref="Request"/>.</summary>
        /// <param name="uri">The text form of a <see cref="Uri"/> which this request represents.</param>
        /// <param name="parent">The request which linked to this <see cref="Uri"/>, or <code>null</code> if this is an initial request.</param>
        public Request(string uri, Request parent) : this(TranslateUri(uri), parent) { }
        /// <summary>Create a <see cref="Request"/> from a string representation of a <see cref="Uri"/> and a parent <see cref="Request"/>.</summary>
        /// <param name="uri">The text form of a <see cref="Uri"/> which this request represents.</param>
        public Request(string uri) : this(TranslateUri(uri), null) { }

        /// <summary>The parent of this request.</summary>
        /// <remarks>The parent is the request which contained the reference to this request.  It is <code>null</code> if this request had no parent.</remarks>
        public Request Parent {
            get { return _Parent; }
        }

        /// <summary>The <see cref="System.Uri"/> of this request.</summary>
        public Uri Uri {
            get { return _Uri; }
        }

        /// <summary>The depth of this request.</summary>
        /// <remarks>The depth of a request is the number of parents that this request has.  
        /// Root requests have depth <code>0</code>.</remarks>
        public int Depth {
            get {
                int depth = 0;
                Request parent = _Parent;
                while (parent != null) {
                    parent = parent.Parent;
                    ++depth;
                }
                return depth;
            }
        }

        /// <summary>Translates a <see cref="String"/> to a <see cref="Uri"/>.</summary>
        /// <param name="uri">The <see cref="String"/> to convert.</param>
        /// <returns>The translated uri, or throws an excpetion if the conversion fails.</returns>
        private static Uri TranslateUri(string uri) {
            if (uri == null) throw new ArgumentNullException("uri");
            if (uri.Length == 0) throw new ArgumentOutOfRangeException("uri", "uri can not be a zero length string.");

            return new Uri(uri, UriKind.RelativeOrAbsolute);
        }

        private static Uri JoinUrisIfNecessary(Uri uri, Request parent) {
            if (parent != null && !uri.IsAbsoluteUri) {
                return new Uri(parent.Uri, uri);
            }
            else {
                return uri;
            }
        }

        #region IComparable<Request> Members
        /// <summary>Compare this request to another using the <see cref="Uri"/> for comparison.</summary>
        /// <param name="other">The other Request to compare.</param>
        /// <returns>The regulr <code>CompareTo</code> return values.</returns>
        public int CompareTo(Request other) {
            return _Uri.ToString().CompareTo(other._Uri.ToString());
        }
        #endregion

        #region Object Overrides
        /// <summary>Returns whether this <see cref="Request"/> is the same as the <see cref="Object"/> passed in.</summary>
        /// <param name="obj">The object to compare with.</param>
        /// <returns><code>true</code> if the objects are the same, or <code>false</code> if not.</returns>
        public override bool Equals(object obj) {
            return this.CompareTo(obj) == 0;
        }

        /// <summary>Returns the hashcode of the Request, based on its <see cref="Uri"/>.</summary>
        /// <returns>The hashcode.</returns>
        public override int GetHashCode() {
            return _Uri.GetHashCode();
        }

        /// <summary>Provides a <see cref="String"/> representation of the request, which is its Uri.</summary>
        /// <returns>A <see cref="String"/> representation of the request, which is its Uri.</returns>
        public override string ToString() {
            return _Uri.ToString();
        }
        #endregion

        #region IComparable Members
        /// <summary>Compare this request to another using the <see cref="Uri"/> for comparison.</summary>
        /// <param name="obj">The other Request to compare.</param>
        /// <returns>The same values regularly returned by <code>CompareTo</code>.</returns>
        public int CompareTo(object obj) {
            Request other = obj as Request;
            if (other == null) return -1;
            return this.CompareTo(other);
        }
        #endregion
        /*
        /// <summary>Compares two requests for equality.</summary>
        /// <param name="leftRequest">The left request.</param>
        /// <param name="rightRequest">The right request.</param>
        /// <returns><code>true</code> if the requests are equal, otherwise <code>false</code>.</returns>
        public static bool operator==(Request leftRequest, Request rightRequest){
            return leftRequest.Equals(rightRequest);
        }

        /// <summary>Compares two requests for inequality.</summary>
        /// <param name="leftRequest">The left request.</param>
        /// <param name="rightRequest">The right request.</param>
        /// <returns><code>true</code> if the requests are unequal, otherwise <code>false</code>.</returns>
        public static bool operator!=(Request leftRequest, Request rightRequest){
            return !leftRequest.Equals(rightRequest);
        }

        /// <summary>Compares two requests to see if the left is less than the right.</summary>
        /// <param name="leftRequest">The left request.</param>
        /// <param name="rightRequest">The right request.</param>
        /// <returns><code>true</code> if the the left request is less than the right, otherwise <code>false</code>.</returns>
        public static bool operator <(Request leftRequest, Request rightRequest) {
            return leftRequest.CompareTo(rightRequest)<0;
        }

        /// <summary>Compares two requests to see if the left is greater than the right.</summary>
        /// <param name="leftRequest">The left request.</param>
        /// <param name="rightRequest">The right request.</param>
        /// <returns><code>true</code> if the the left request is greater than the right, otherwise <code>false</code>.</returns>
        public static bool operator >(Request leftRequest, Request rightRequest) {
            return leftRequest.CompareTo(rightRequest) > 0;
        }*/

    }
}
